還記得前面學習 ImageSendMessage、TextSendMessage,以及 FlexSendMessage 的時候,我們都是用什麼樣的事件觸發嗎?
# 處理訊息的事件
@parser.add(MessageEvent, message=TextMessage)
def handle_message(event):
答案是
MessageEvent,指的是使用者發送了文字、圖片、影片、貼圖等訊息,就會觸發MessageEvent,而TextMessage則是限定這個函數只處理文字訊息。
無論在 Day 06 歸剛回貓貓機器人、Day 09 陽春麵版查評價關鍵字 以及 Day 10 金箔陽春麵版查評價關鍵字,都是藉由使用者輸入任意文字訊息,去觸發 handle_message 函式。
而如果今天想要讓使用者按我發送的 FlexSendMessage 裡頭的按鈕來做回覆呢?畢竟懶得打字可說是大家的共識,能用選的就用選的~
像是醬子,透過
FlexSendMessage按鈕讓使用者不用再同時輸入課名與老師名,這設計可真是趕時間跑加簽的福音(?
有別於前面提到的 MessageEvent,PostbackEvent 處理的就是使用者點擊含有回傳資料的按鈕。
FlexSendMessage 方式回覆建立一個 JSON 檔案: 先將課程列表回覆模板 JSON 的內容放到一個新檔案:hulolo > chatbot > reply_course_list.json
引入 PostbackEvent 套件:
# hulolo > chatbot > views.py
from linebot.models import MessageEvent, PostbackEvent, TextMessage, 
    TextSendMessage, ImageSendMessage, FlexSendMessage
使用者輸入老師名的處理邏輯: 重新寫一個觸發事件的函式 handle_teacher_name_msg,用來處理使用者輸入老師名。邏輯是:
teacher_lists_flex_message_package 套漂亮的模板# hulolo > chatbot > views.py
@parser.add(MessageEvent, message=TextMessage)
def handle_teacher_name_msg(event):    
    user_message = event.message.text  # 取得使用者發送的文字
    filtered_teacher = Course.objects.filter(teacher_name=user_message)
    if filtered_teacher.exists():
        teacher_name = user_message
        # values_list 是以陣列裝課程名稱
        # distinct 可以把可能重複的老師名做過濾
        candidate_courses = filtered_teacher.values_list(
        'course_name', flat=True).distinct()
        # 稍後會製作這個漂亮的函式,先呼叫它
        flex_message = teacher_lists_flex_message_package(
        teacher_name, candidate_courses)
        message = FlexSendMessage(
                            alt_text=f"{teacher_name} 老師的課程",
                            contents=flex_message
                        )
        line_bot_api.reply_message(
                    event.reply_token,
                    message)
    else:
        print("Empty")   
處理漂亮的課程列表 Flex message 回覆: 新增 teacher_lists_flex_message_package 函式處理漂亮的課程列表 Flex message 回覆,這邊要特別介紹 Flex message 若有按鈕,是需要在裡面加上按鈕按下去會傳遞的資料,也就是註解中按鈕背後藏著真正的資料 (詳見程式碼內的註解)。
# hulolo > chatbot > views.py
def teacher_lists_flex_message_package(teacher_name, candidate_courses):
    # 引入 JSON 檔案
    json_path = os.path.join(BASE_DIR, 
        'chatbot', 'reply_course_list.json')
    flex = json.load(open(json_path, 'r', encoding='utf-8'))    
    # 設定標題為老師名稱
    flex['body']['contents'][0]['text'] = f"{teacher_name}老師的哪堂課?"
    # 課程列表的選項用了十個顏色!
    colors = ['#F0C29E', '#A1DE95', '#F5C578', '#91D9C2', '#DFC493', 
            '#F0C29E', '#A1DE95', '#F5C578', '#91D9C2', '#DFC493']
    # 根據上述顏色動態生成課程按鈕,而且是根據幾堂課就產生幾個按鈕~
    for i, course in enumerate(candidate_courses, start=1):
        button = {
            'type': 'box',
            'layout': 'vertical',
            'spacing': 'none',
            'contents': [{
                'type': 'button',
                'style': 'primary',
                'action': {
                    'type': 'postback',
                    'label': course,
                    #注意! 這很重要,此處代表按鈕背後真正的資料
                    'data': f"{teacher_name}-{course}" 
                },
                'color': colors[i-1],
                'margin': 'xs',
                'offsetTop': 'none',
                'height': 'sm'
            }],
            'flex': 0,
            'borderWidth': 'medium',
            'cornerRadius': 'xxl',
            'offsetTop': 'none',
            'margin': 'md',
            'backgroundColor': colors[i-1]
        }
        flex['body']['contents'].append(button)
    return flex
使用者按下後的 PostbackEvent 處理: 新增一個函式 handle_postback,這邊就會使用到 PostbackEvent,用來觸發與分析剛剛按鈕內的資料,並進行查詢回覆後告知評價內容,讀者可以觀察其與 handle_message 函式的差異,會發現幾乎相同哦~
# hulolo > chatbot > views.py
@parser.add(PostbackEvent)
def handle_postback(event):
    # 取得使用者點按鈕時回傳的資料
    postback_data = event.postback.data
    # 還記得前面提及按鈕背後的資料嗎? 在這!
    # postback_data 格式為 "課程名稱-老師名稱"
    if "-" in postback_data:
        teacher_name = postback_data.split("-")[0]
        course_name = postback_data.split("-")[1]
        filtered_courses = Course.objects.filter(
            teacher_name=teacher_name, course_name=course_name)[:5]
        if filtered_courses.exists():
            messages = []
            # 使用 enumerate 給每個課程加上編號
            for idx, course in enumerate(filtered_courses, start=1):  
                # 準備課程資料以傳入 flex_message_package 函式
                course_info = {
                    'course_name': course.course_name,
                    'teacher_name': course.teacher_name,
                    'course_type': course.course_type,
                    'feedback_content': course.feedback_content,
                    'evaluation_semester': course.evaluation_semester,
                    'submitter_name': course.submitter_name,
                    'number': str(idx),  # 這裡的 idx 是第幾則的意思
                    }
                # 呼叫 flex_message_package 函式來產生 Flex Message
                flex_message = flex_message_package(course_info)
                # 使用 FlexSendMessage 回傳 Flex Message
                messages.append(FlexSendMessage(
                        alt_text=f"課程評價:{course.course_name}",
                        contents=flex_message
                    ))
            line_bot_api.reply_message(
                event.reply_token,
                messages  # 回傳 Flex Message 列表
            )         
        else:
            print("Empty")
    else:
        print("Empty")
當輸入 吳小峰 就會得到一筆訊息回覆,包含一個 特殊教育學生評量 的課程~可以到後台試著增加同老師不同課程,就會有多的按鈕出現 (在這邊教學方便我們上限設為 10 種不同課程):
handle_message 與 handle_postback 內容很相近時,可以考慮將其回覆評價內容的邏輯,獨立一個函式,就能避免重複造輪子的問題。在這篇文章中,我們學會了:
PostbackEvent 觸發事件,在這邊是按下 Flex message 裡的按鈕觸發